package br.ufpb.di.ppgi.persistencia.domainstore;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;

import br.com.caelum.vraptor.ioc.Component;
import br.ufpb.di.ppgi.persistencia.PersistenceExceptionHandler;

@Component
public class DomainStoreImpl implements DomainStore {
	
	private final EntityManager manager;

	public DomainStoreImpl(EntityManager manager,DomainStoreConfiguration conf) {
		this.manager = manager;
		if (conf != null) {
			setAutoCommit(conf.isAutoCommit());
			setAutoRollback(conf.isAutoRollback());
		}
	}
	
	private EntityTransaction transaction;
	private boolean autoCommit = false;
	private boolean autoRollback = true;
	

	private boolean startTransactionIfNeed() {
		if (isTrasactionActive() || !isAutoCommit())
			return false;
		
		beginTransaction();
		return true;
	}
	
	@Override
	public boolean isAutoCommit() {
		return autoCommit;
	}
	@Override
	public void setAutoCommit(boolean autoCommit){
		this.autoCommit = autoCommit;
	}
	@Override
	public boolean isAutoRollback() {
		return autoRollback;
	}
	@Override
	public void setAutoRollback(boolean autoRollback) {
		this.autoRollback = autoRollback;
	}

	public void beginTransaction() {
		if (isTrasactionActive())
			throw new IllegalStateException("Transação já estava iniciada.");
		else
			transaction.begin();
	}

	public void commit() {
		if (isTrasactionActive()) {
			transaction.commit();
			transaction = null;
		} else {
			transaction = null;
			throw new IllegalStateException("Transação não está ativa");
		}
	}

	public boolean isTrasactionActive() {
		return (transaction != null) ? 
				transaction.isActive() : 
				(transaction = manager.getTransaction()).isActive();
	}

	public void rollback() {
		if (isTrasactionActive()) {
			transaction.rollback();
			transaction = null;
		} else
			throw new IllegalStateException("Transação não está ativa");
	}

	public <T> T get(Class<T> target, Object id) {
		return manager.find(target, id);
	}

	public void add(Object entity) {
		final boolean wasStarted = startTransactionIfNeed();
		try {
			manager.persist(entity);
		} catch (RuntimeException e) {
			if (wasStarted && isAutoRollback())
				rollback();
			throw new PersistenceExceptionHandler().handle(e);
		}
	}

	@Override
	public EntityManager getDelegate() {
		return manager;
	}
	
	public Session getSession() {
		return (Session) getDelegate().getDelegate();
	}
	
//	@Override
//	@SuppressWarnings("unchecked")
//	public <T> List<T> list(Class<T> target) {
//		return getSession().createCriteria(target).list();
//	}
	
	@Override
	@SuppressWarnings("unchecked")
	public <T> List<T> list(Class<T> target,String ... orders) {
		Criteria c = getSession().createCriteria(target);
		for (String property : orders) {
			c.addOrder(Order.asc(property));
		}
		return c.list();
	}

	@Override
	public <T> T remove(Class<T> target, Object id) {
		T entity = get(target, id);
		if (entity != null)
			remove(entity);
		return entity;
	}
	@Override
	public void remove(Object entity) {
		final boolean wasStarted = startTransactionIfNeed();
		try {
			manager.remove(entity);
		} catch (RuntimeException e) {
			if (wasStarted && isAutoRollback())
				rollback();
			throw new PersistenceExceptionHandler().handle(e);
		}
	}

	@Override
	public <T> T merge(T entity) {
		final boolean wasStarted = startTransactionIfNeed();
		try {
			return manager.merge(entity);
		} catch (RuntimeException e) {
			if (wasStarted && isAutoRollback())
				rollback();
			throw new PersistenceExceptionHandler().handle(e);
		}
	}

	@Override
	public void addAll(Object... entitys) {
		addAll(Arrays.asList(entitys));
	}

	@Override
	public void addAll(Collection<?> entitys) {
		final boolean wasStarted = startTransactionIfNeed();
		try {
			for (Object entty : entitys) {
				manager.persist(entty);
			}
		} catch(Exception ex) {
			if (wasStarted && isAutoRollback())
				rollback();
			throw new PersistenceExceptionHandler().handle(ex);
		}
	}
}
